package com.beiding.config;

import com.beiding.authorization.wechat.WeChatAuthorizationFilter;
import com.beiding.service.user.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy;
import org.springframework.session.data.redis.RedisOperationsSessionRepository;
import org.springframework.session.security.SpringSessionBackedSessionRegistry;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true, proxyTargetClass = true)
class WebSecurityConfig extends WebSecurityConfigurerAdapter {


    //用户认证服务
    private UserService userService;

    //认证提供者,提供出表单之外的其他认证方式
    private List<AuthenticationProvider> providers;

    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    @Autowired
    public void setProviders(List<AuthenticationProvider> providers) {
        this.providers = providers;
    }


    //基于Redis的Session注册表
    private SpringSessionBackedSessionRegistry sessionRegistry;

    //基于Redis的Session仓库
    private RedisOperationsSessionRepository repository;

    @Autowired
    public void setRepository(RedisOperationsSessionRepository repository) {
        this.repository = repository;
        sessionRegistry = new SpringSessionBackedSessionRegistry<>(repository);

    }

    //提供微信登录
    @Bean
    WeChatAuthorizationFilter weChatAuthorizationFilter() throws Exception {
        WeChatAuthorizationFilter filter = new WeChatAuthorizationFilter();
        filter.setAuthenticationManager(authenticationManager());
        return filter;
    }


    //基于BCrypt的加密算法
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }


    //配置认证方式

    /*
        注意:
            必须确保Session中的SPRING_SECURITY_CONTEXT属性能够反序列化成功.
            该属性存放了当前用户的所有认证信息.否则即便实现Session共享也做不到单点登录

            本项目中,为了确保上一点所有服务都含有com.beiding.pojo.auth.User以及com.beiding.pojo.auth.AuthorityLocal
            两个类且serialVersionUID对所有的服务都相同


     */
   @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        providers.forEach(authenticationProvider -> {
            auth.authenticationProvider(authenticationProvider);
        });

        auth.userDetailsService(userService).passwordEncoder(passwordEncoder());

    }

    //http拦截配置
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .addFilterBefore(weChatAuthorizationFilter(), UsernamePasswordAuthenticationFilter.class)   //微信登录
                .authorizeRequests()
                .antMatchers("/manager/accredit","/","/home","/user/login/**", "/user/sendCode", "/user/register/**", "/user/retrieve-password/**", "/js/*", "/css/*").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/user/login")
                .successHandler(new SavedRequestAwareAuthenticationSuccessHandler() {
                    @Override
                    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {

                        //在认证成功后将用户名以及权限直接放入session中
                        request.getSession().setAttribute("user.name", authentication.getName());
                        request.getSession().setAttribute("user.authorities", authentication.getAuthorities());

                        super.onAuthenticationSuccess(request, response, authentication);
                    }
                })
                .permitAll()
                .failureUrl("/user/login?error")
                .and().logout().logoutUrl("/logout").logoutSuccessUrl("/")
                .and().csrf().disable();


        http.sessionManagement()
                .sessionAuthenticationStrategy(new ConcurrentSessionControlAuthenticationStrategy(
                        sessionRegistry
                ))
                .invalidSessionUrl("/user/login")
                .maximumSessions(1)
                .expiredUrl("/user/login");

    }


}
